package application

import (
	"encoding/json"
	"errors"
	"gitee.com/dh114007/wechat_token/config"
	"gitee.com/dh114007/wechat_token/core/httputils"
	"gitee.com/dh114007/wechat_token/driver/cache"
	"gitee.com/dh114007/wechat_token/utils/request"
	"net/http"
	"strconv"
)

const WeixinTokenApi = "https://api.weixin.qq.com/cgi-bin/token"
const WeixinTicketApi = "https://api.weixin.qq.com/cgi-bin/ticket/getticket"
const WeixinJSCodeApi = "https://api.weixin.qq.com/sns/jscode2session"

type WeixinRet struct {
	ErrCode int64  `json:"errcode"`
	ErrMsg  string `json:"errmsg"`
}

type WeixinTokenRet struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int64  `json:"expires_in"`
}

type WeixinTicketRet struct {
	Ticket    string `json:"ticket"`
	ExpiresIn int64  `json:"expires_in"`
}

type WeixinSecretRet struct {
	Secret string `json:"secret"`
}

type WeixinJSCode2SessionRet struct {
	OpenID     string `json:"openid"`
	SessionKey string `json:"session_key"`
	UnionID    string `json:"unionid"`
}

func requestAccessToken(appid string) (*WeixinTokenRet, error) {
	if cacheToken := cache.Get(appid); nil != cacheToken {
		return &WeixinTokenRet{
			AccessToken: cacheToken.(string),
			ExpiresIn:   cache.TTL(appid),
		}, nil
	}
	secret, ok := config.Conf.Wechat.Keys[appid]
	if ok == false {
		return nil, &httputils.HttpStatusError{
			Code: http.StatusForbidden,
			Err:  errors.New("appid not exits"),
		}
	}
	querys := []string{"grant_type=client_credential", "appid=" + appid, "secret=" + secret}
	body, err := request.Get(WeixinTokenApi, querys)
	if err != nil {
		return nil, err
	}
	var resp struct {
		WeixinRet
		WeixinTokenRet
	}
	err = json.Unmarshal(body, &resp)
	if err != nil {
		return nil, err
	}
	if resp.ErrCode != 0 {
		return nil, &httputils.HttpStatusError{
			Code: http.StatusNotFound,
			Err:  errors.New(strconv.FormatInt(resp.ErrCode, 10) + ":" + resp.ErrMsg),
		}
	}
	cache.Set(appid, resp.AccessToken, 7000)
	return &resp.WeixinTokenRet, nil
}

func requestJsApiTicket(appid string) (*WeixinTicketRet, error) {
	cacheKey := "ticket:" + appid
	if cacheTicket := cache.Get(cacheKey); nil != cacheTicket {
		return &WeixinTicketRet{
			Ticket:    cacheTicket.(string),
			ExpiresIn: cache.TTL(cacheKey),
		}, nil
	}

	tokenRet, err := requestAccessToken(appid)
	if err != nil {
		return nil, err
	}

	querys := []string{"type=jsapi", "access_token=" + tokenRet.AccessToken}
	body, err := request.Get(WeixinTicketApi, querys)
	if err != nil {
		return nil, err
	}
	var resp struct {
		WeixinRet
		WeixinTicketRet
	}
	err = json.Unmarshal(body, &resp)
	if err != nil {
		return nil, err
	}
	if resp.ErrCode != 0 {
		return nil, &httputils.HttpStatusError{
			Code: http.StatusNotFound,
			Err:  errors.New(strconv.FormatInt(resp.ErrCode, 10) + ":" + resp.ErrMsg),
		}
	}
	cache.Set(cacheKey, resp.Ticket, 7000)
	return &resp.WeixinTicketRet, nil
}

func requestJSCode2Session(appid string, code string) (*WeixinJSCode2SessionRet, error) {
	secret, ok := config.Conf.Wechat.Keys[appid]
	if ok == false {
		err := &httputils.HttpStatusError{
			Code: http.StatusForbidden,
			Err:  errors.New("appid not exits"),
		}
		return nil, err
	}
	querys := []string{"grant_type=authorization_code", "js_code=" + code, "appid=" + appid, "secret=" + secret}
	body, err := request.Get(WeixinJSCodeApi, querys)
	if err != nil {
		return nil, err
	}
	var resp struct {
		WeixinRet
		WeixinJSCode2SessionRet
	}
	err = json.Unmarshal(body, &resp)
	if err != nil {
		return nil, err
	}
	if resp.ErrCode != 0 {
		err = &httputils.HttpStatusError{
			Code: http.StatusNotFound,
			Err:  errors.New(strconv.FormatInt(resp.ErrCode, 10) + ":" + resp.ErrMsg),
		}
		return nil, err
	}
	return &resp.WeixinJSCode2SessionRet, nil
}
